<template>
  <div>
    <n-drawer v-model:show="drawerShow" :width="1000">
      <n-drawer-content title="声音列表" closable>
        <n-row>
          <n-col :span="6">
            <n-form-item label="类型">
              <n-select :options="voiceTypeOptions" v-model:value="voiceQueryForm.type" @update:value="getVoiceList"
                        clearable></n-select>
            </n-form-item>
          </n-col>
          <n-col :span="6">
            <n-form-item label="语言">
              <n-select :options="languageTypeOptions" v-model:value="voiceQueryForm.locale"
                        @update:value="getVoiceList" style="margin-left: 5px;" clearable></n-select>
            </n-form-item>
          </n-col>
          <n-col :span="6">
            <n-form-item label="年龄段">
              <n-select :options="ageTypeOptions" v-model:value="voiceQueryForm.age" @update:value="getVoiceList"
                        style="margin-left: 5px;" clearable></n-select>
            </n-form-item>
          </n-col>
        </n-row>
        <n-grid x-gap="12" :cols="4">
          <n-gi v-for="(voice,index) in voiceList" style="margin-top: 20px">
            <n-card :title="voice.localName" size="small">
              <template #header-extra>
                {{ voice.gender === 'Female' ? '女' : '男' }} {{ voice.age }}
              </template>
              <n-row>
                <n-col :span="16">
                  <n-ellipsis> {{ voice.localeNameZh }}</n-ellipsis>
                </n-col>
                <n-col :span="6" v-if="voice.type=='free'">
                  <n-tag :bordered="false" size="small" type="primary">Edge朗读</n-tag>
                </n-col>
                <n-col :span="6" v-else>
                  <n-tag :bordered="false" size="small" type="warning">微软语音</n-tag>
                </n-col>
              </n-row>
              <n-ellipsis line-clamp="2">
                <n-text type="success" strong>{{ voice.descriptionZh }}</n-text>
              </n-ellipsis>
              <template #footer>
                <n-tag type="primary" v-if="voice?.styleOptions?.length > 0 ">{{ voice.styleOptions.length }}种情绪
                </n-tag>
                <n-tag style="margin-left: 5px" type="success" v-if="voice?.roleOptions?.length > 0 ">
                  {{ voice.roleOptions.length }}种角色
                </n-tag>
              </template>
              <template #action>
                <n-space>
                  <n-button size="small" type="primary" @click="choseVoice(voice)">选择</n-button>
                  <n-button size="small" type="success" v-if="voice?.exampleUrl" @click="playExample(voice) "> 试听
                  </n-button>
                </n-space>

              </template>
            </n-card>
          </n-gi>
        </n-grid>
      </n-drawer-content>
    </n-drawer>
    <n-row>
      <n-col span="16">
        <n-spin :show="formatLoading">
          <n-card title="配音内容" :style="{height: clientHeight - (layoutHeaderHeight  + tabsHeight + 10) + 'px'}">
            <template #header-extra>
              <n-button type="primary" @click="removePunctuation">懒人格式化</n-button>
            </template>
            <n-tabs type="line" animated v-model:value="tabValue">
              <n-tab-pane name="content" tab="文本内容">

                <n-input
                    v-if="props.model=='short'"
                    show-count
                    :maxlength="5000"
                    type="textarea"
                    v-model:value="voiceOptionForm.content"
                    :readonly="storyLock"
                    :style="{height: clientHeight - (layoutHeaderHeight  + tabsHeight + 180) + 'px'}"
                >
                </n-input>

                <n-input
                    v-else
                    show-count
                    type="textarea"
                    v-model:value="voiceOptionForm.content"
                    :readonly="storyLock"
                    :style="{height: clientHeight - (layoutHeaderHeight  + tabsHeight + 180) + 'px'}"
                >
                </n-input>
              </n-tab-pane>
            </n-tabs>
          </n-card>
        </n-spin>
      </n-col>
      <n-col span="8" style="padding-left: 5px">
        <n-card title="设置" :style="{height: clientHeight - (layoutHeaderHeight  + tabsHeight + 10) + 'px'}">
          <div>
            <audio
                v-if="props.model=='short'"
                ref="audioPlayer"
                :autoplay="true"
                controls
                style="width: 100%"
            ></audio>
            <n-grid :x-gap="5">
              <n-grid-item :span="18">
                <n-input
                    style="width: 100%;margin-top: 10px"
                    placeholder="语音名称"
                    v-model:value="voiceOptionForm.taskName"
                >
                </n-input>
              </n-grid-item>
              <n-grid-item :span="6">
                <n-button
                    type="primary"
                    :loading='createLoading'
                    style="width: 100%;margin-top: 10px"
                    @click="voiceGenerate"
                >
                  生成
                </n-button>
              </n-grid-item>
            </n-grid>

            <n-grid :x-gap="10">
              <n-grid-item :span="12">
                <n-button
                    v-if="fileId?.length>0"
                    circle
                    type="warning"
                    style="width: 100%;margin-top: 10px"
                    @click="downloadAudio"
                >
                  下载语音
                </n-button>
              </n-grid-item>

              <n-grid-item :span="12">
                <n-button
                    v-if="fileId?.length>0"
                    circle
                    type="warning"
                    style="width: 100%;margin-top: 10px"
                    @click="downloadSRT"
                    :loading="downloadSRTLoading"
                >
                  下载SRT
                </n-button>
              </n-grid-item>
            </n-grid>


          </div>
          <n-divider/>
          <n-row>
            <n-col span="18">
              <n-card v-if="selectVoice" :title="selectVoice.localName" size="small">
                <template #header-extra>
                  {{ selectVoice.gender === 'Female' ? '女' : '男' }} {{ selectVoice.age }}
                </template>
                {{ selectVoice.localeNameZh }}
                <template #footer>
                  <n-row>
                    <n-col span="12" v-if="selectVoice?.styleOptions?.length > 0 ">
                      <n-form-item label="情绪">
                        <n-select v-model:value="voiceOptionForm.voiceStyle" :options="selectVoice.styleOptions"
                                  placeholder="请选择情绪"></n-select>
                      </n-form-item>
                    </n-col>
                    <n-col span="12" v-if="selectVoice?.roleOptions?.length > 0 ">
                      <n-form-item label="角色">
                        <n-select style="padding-left: 5px;" v-model:value="voiceOptionForm.voiceRole"
                                  :options="selectVoice.roleOptions" placeholder="请选择情绪"></n-select>
                      </n-form-item>
                    </n-col>
                  </n-row>
                </template>
                <template #action>
                  <n-button size="small" type="primary" @click="drawerShow=true">切换声音</n-button>
                </template>
              </n-card>
            </n-col>
          </n-row>
          <n-divider/>
          <n-form>
            <n-grid :x-gap="10" :y-gap="10">
              <n-grid-item :span="12">
                <n-form-item label="音速">
                  <n-input-number v-model:value="voiceOptionForm.rate" :min="0.8" :max="2" :step="0.1"/>
                </n-form-item>
              </n-grid-item>
              <n-grid-item :span="12">
                <n-form-item label="音量">
                  <n-input-number v-model:value="voiceOptionForm.volume" :min="0.8" :max="2" :step="0.1"/>
                </n-form-item>
              </n-grid-item>
              <n-grid-item :span="12">
                <n-form-item label="音调">
                  <n-input-number v-model:value="voiceOptionForm.pitch" :min="0.8" :max="2"
                                  :step="0.1"></n-input-number>
                </n-form-item>
              </n-grid-item>
              <!--               <n-grid-item :span="12" v-if="selectVoice?.type==='paid'">-->
              <!--                 <n-form-item label="全局停顿">-->
              <!--                   <n-select :options="pauseTimeOptions" v-model:value="voiceOptionForm.pauseTime"></n-select>-->
              <!--                 </n-form-item>-->
              <!--               </n-grid-item>-->
            </n-grid>
          </n-form>
        </n-card>
      </n-col>
    </n-row>
  </div>
</template>
<script setup lang="ts">
import {voiceController} from '@/api/voice/ttsController.api'
import {useMessage} from 'naive-ui'
import {onMounted, ref, reactive} from "vue"
//@ts-ignore
import fileFunction from '@/libs/globalFunction/file.js'
import {storeToRefs} from "pinia";
import {useSystemStore} from "@/store/modules/useSystemStore"
import {ttsDict} from '@/dictionary/ttsDict'
import {useUserStore} from "@/store/modules/useUserStore";
import {store} from "@/store";
import {storage} from "@/hooks/useStorage";
import {whisperController} from "@/api/controller/media/whisperController";
import {storyController} from "@/api/story/story.api";
import {promptControllerApi} from "@/api/prompt/prompt.api";

const {authorization, tokenKey} = storeToRefs(useUserStore(store));
const token = authorization?.value ?? storage.get(tokenKey?.value as string);

const {
  clientHeight,
  layoutHeaderHeight,
  collapsed,
  darkTheme,
  tabsHeight
} = storeToRefs(useSystemStore());
const {voiceTypeOptions, languageTypeOptions, ageTypeOptions, pauseTimeOptions} = ttsDict()

const createLoading = ref(false)
const message = useMessage()
const story = ref("")
const storyLock = ref(false)
const tabValue = ref("content")
const voiceList = ref([])
const formatLoading = ref(false)
const drawerShow = ref(false)
const defaultVoice = ref({})
const selectVoice = ref(null)
const audioPlayer = ref(null)
const fileId = ref('')
const exampleUrl = ref('')
const downloadSRTLoading = ref(false)
const voiceQueryForm = reactive({
  age: '',
  type: 'free',
  locale: ''
})


const voiceOptionForm = reactive({
  taskName: '',
  content: '',
  ssml: '',
  voiceName: 'Yunxi',
  voiceStyle: 'default',
  voiceRole: 'default',
  rate: 1,
  volume: 1,
  pitch: 1,
  ttsType: '',
  pauseTime: '300'
})


onMounted(() => {
  getVoiceList()
})


const props = defineProps({
  closeTTSMaker: {
    type: Function,
    default: () => {
    },
    required: true
  },
  model: {
    type: String,
    required: true,
    default: 'short'
  }
})


const getVoiceList = () => {
  voiceController.listVoiceActors(voiceQueryForm).then(res => {
    if (res.code === 200) {
      voiceList.value = res.data
      defaultVoice.value = voiceList.value[0]
      selectVoice.value = voiceList.value[0]
    } else {
      message.error(res.msg)
    }
  })
}

const choseVoice = (voice: any) => {
  selectVoice.value = voice
  drawerShow.value = false
  voiceOptionForm.voiceName = voice.shortName
  voiceOptionForm.voiceRole = 'default'
  voiceOptionForm.voiceStyle = 'default'
}

const voiceGenerate = () => {
  if (voiceOptionForm.content === '') {
    message.error('请填写配音内容')
    return
  }
  if (voiceOptionForm.taskName===''){
    message.error('请填写语音名称')
    return
  }

  const groupTexts = voiceOptionForm.content.split(/\n/);
  const isWarning = ref(false)
  groupTexts.forEach((groupText) =>{
    if (groupText.trim().length > 25){
      isWarning.value = true
    }
  })
  if (isWarning.value){
    window.$dialog.warning({
      title: "段落过长",
      content: "检测到有段落语音过长(超过25字),会导致字幕过长，分镜困难。您可以点击懒人格式化进行格式化,或者自行处理一下。",
      positiveText: "我还是处理一下",
      negativeText: "不用管继续生成",
      onPositiveClick: () => {
        return
      },
      onNegativeClick:()=>{
        startVoiceGenerateTask()
      }
    })
  }else {
    startVoiceGenerateTask()
  }
}


const startVoiceGenerateTask = () =>{
  voiceOptionForm.voiceName = selectVoice.value.shortName
  voiceOptionForm.ttsType = selectVoice.value.type
  voiceController.voiceGenerate(voiceOptionForm).then(res => {
    createLoading.value = false
    if (res.code === 200) {
      fileId.value = res.data
      props.closeTTSMaker()
    } else {
      message.error(res.msg)
    }
  })
}

const voicePreview = () => {
  voiceOptionForm.voiceName = selectVoice.value.shortName
  voiceOptionForm.ttsType = selectVoice.value.type
  createLoading.value = true
  voiceController.voicePreview(voiceOptionForm).then(res => {
    createLoading.value = false
    if (res.code === 200) {
      audioPlayer.value.src = fileFunction.getViewFilePath({userFileId: res.data})
      audioPlayer.value.play();
      fileId.value = res.data
    } else {
      message.error(res.msg)
    }
  })
}

const downloadAudio = () => {
  const row = {
    userFileId: fileId.value,
    name: new Date()
  }
  let link = document.createElement("a")
  link.style.display = 'none'
  link.href = fileFunction.getDownloadFilePath(row) + "&token=" + token
  link.setAttribute('download', row.name)
  document.body.appendChild(link)
  link.click()
}

const downloadSRT = () => {

  downloadSRTLoading.value = true
  const row = {
    userFileId: fileId.value,
    name: new Date()
  }
  whisperController.getText(row.userFileId).then((res) => {
    if (res.code === 200) {
      const txtContents = res.data
      let srt = '';
      let index = 1;
      for (let i = 0; i < txtContents.length; i++) {
        let start = convert(txtContents[i].startTime);
        let end = convert(txtContents[i].endTime);
        let text = txtContents[i].videoText;
        srt += index + '\n';
        srt += start + ' --> ' + end + '\n';
        srt += text + '\n\n';
        index++;
      }
      downloadSRTFile(srt, row.name)
    }
  }).finally(() => {
    downloadSRTLoading.value = false
  })
}

function downloadSRTFile(srt: string, name: string) {
  let element = document.createElement('a');
  element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(srt));
  element.setAttribute('download', name + '.srt');
  element.style.display = 'none';
  document.body.appendChild(element);
  element.click();
  document.body.removeChild(element);
}

function convert(secondsstr: number) {
  const hours = Math.floor(secondsstr / 3600);
  const minutes = Math.floor((secondsstr % 3600) / 60);
  const seconds = Math.floor(secondsstr % 60);
  const milliseconds = (secondsstr.toString().split('.')[1] || '000').padEnd(3, '0');
  // Format as two-digit strings
  const hoursStr = String(hours).padStart(2, '0');
  const minutesStr = String(minutes).padStart(2, '0');
  const secondsStr = String(seconds).padStart(2, '0');
  const millisecondsStr = String(milliseconds).padStart(3, '0');
  // SRT time format: hh:mm:ss,mmm
  return `${hoursStr}:${minutesStr}:${secondsStr},${millisecondsStr}`
}


const playExample = (voice: any) => {

  audioPlayer.value.src = voice.exampleUrl
  audioPlayer.value.play();

}

const removePunctuation = () => {
  const content = voiceOptionForm.content;
  let text = content.replace(/“/g, ',').replace(/”/g, ',').replace(/:/g, ',').replace(/：/g, ',').replace(/"/g, ',');
  const sentenceEndRegex = /[。！？：]/;
  const commaRegex = /，|,/;
  const paragraphs = text.split(/\n/);
  // 存储最终切割后的字符串数组
  const finalContent = [];
  // 遍历每个段落
  paragraphs.forEach(paragraph => {
    // 如果段落长度超过15个字符，则进一步按句子进行切割
    if (paragraph.length > 15) {
      // 按句子结束标点符号切割段落
      const sentences = paragraph.split(sentenceEndRegex);
      let tempSentence = '';

      // 遍历每个句子
      sentences.forEach(sentence => {
        // 去除可能的前后空格
        const trimmedSentence = sentence.trim();

        // 如果句子长度大于15个字符，则按逗号进行切割
        if (trimmedSentence.length > 15) {
          // 切割句子为短句
          const subSentences = trimmedSentence.split(commaRegex);
          subSentences.forEach(sub => {
            // 如果当前短句加上新短句长度超过15，则将当前短句加入结果数组
            if ((tempSentence.length + sub.length) > 15) {
              finalContent.push(tempSentence.trim());
              tempSentence = sub;
            } else {
              tempSentence += sub;
            }
          });
        } else {
          // 否则直接添加到临时句子
          tempSentence += trimmedSentence;
        }

        // 如果当前临时句子长度超过15，则加入结果数组并重置
        if (tempSentence.length > 15) {
          finalContent.push(tempSentence.trim());
          tempSentence = '';
        }
      });
      // 将剩余的临时句子加入结果数组
      if (tempSentence.length > 0) {
        finalContent.push(tempSentence.trim());
      }
    } else {
      // 如果段落长度不超过15个字符，直接添加到结果数组
      finalContent.push(paragraph);
    }
  });


  const sentences_remove_biaodianfuhao = ref([])
  const regex = /[.。!！?？，,…]/g;
  finalContent.forEach( (value) => {

    const sentence_new = ref([])
    const sentences = value.split(regex);
    sentences.forEach((sentence:any) => {
      if (sentence !== '' && sentence !== '\n') {
        sentence_new.value.push(sentence.trim())
      }
    })

    if (sentence_new.value.join(",").trim() !== '' && sentence_new.value.join(",").trim() !== '\n'){
      sentences_remove_biaodianfuhao.value.push(sentence_new.value.join("，"));
    }
  })
  voiceOptionForm.content = sentences_remove_biaodianfuhao.value.join('\n')
}

const sentenceBreak = () =>{
  const param = {
    sentense: voiceOptionForm.content,
    breakLength: 15
  }
  storyController.sentenseBreak(param).then(res => {
    if (res.code === 200) {
      voiceOptionForm.content = res.data

    } else {
      message.error(res.msg)
    }
  })
}
</script>
